﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Drawing.Imaging;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Configuration;
using System.Diagnostics;

/* Dynamic Image Processor 
 * Author: Brad Foley
 * 
 */
namespace DynamicImage
{
  
  public sealed class DynamicImageProcessor : IHttpHandler
  {
    DynamicImageProcessorConfig config;
    string mimeType = ""; 
    string cacheFile = "";
    bool IHttpHandler.IsReusable
    {
      get { return false; }
    }

    void IHttpHandler.ProcessRequest(HttpContext context)
    {
      // load the configuration from web.config
      config = (DynamicImageProcessorConfig)ConfigurationManager.GetSection("DynamicImageSection");
      mimeType = config.DefaultMimeType;
      Boolean useCache = config.Cache.Enabled;  
      // Setup the Response, Clear out all the fun stuff
      context.Response.Clear();
      context.Response.BufferOutput = true;

      // get the orginally requested url and the attached height and width request. Then set to 0 if not present
      string imageUrl = context.Request.Path;
      int imageHeight = (context.Request.QueryString["h"] != null ? Convert.ToInt32(context.Request.QueryString["h"]) : 0);
      int imageWidth = (context.Request.QueryString["w"] != null ? Convert.ToInt32(context.Request.QueryString["w"]) : 0);

      // lets check and see if we've already used this resolution before
      if (useCache)
      {
        cacheFile = (config.Cache.CacheDirectory + imageUrl.Replace("/", "\\")).Substring(0, (config.Cache.CacheDirectory + imageUrl.Replace("/", "\\")).LastIndexOf(".jpg")) + "\\" + imageHeight + "." + imageWidth + ".jpg";
        if (imageHeight != 0 && imageWidth != 0)
          if (System.IO.File.Exists(cacheFile))
          {
            // sends the previously generated image
            context.Response.ContentType = mimeType;
            context.Response.WriteFile(cacheFile);
            context.Response.Flush();
            context.Response.End();
            return;
          }
      }
      // The Original Image object
      System.Drawing.Image imageFullSize; 

      // Lets see if the requested file even exists
      if (!System.IO.File.Exists(context.Server.MapPath(imageUrl)))
      {
        // it doesn't, so lets see if were using a not found image        
        if (config.EnableImageNotFound)
        {
          // sends the not found image
          context.Response.ContentType = mimeType; 
          context.Response.WriteFile(context.Server.MapPath(config.ImageNotFound.FileName));
          context.Response.Flush();
          context.Response.End();
          return;
        }
        else
        {
          // sends a 404
          context.Response.Clear();
          context.Response.StatusCode = 404;
          context.Response.End();
          return;
        }
      }

      // lets check to see if requesting a custom resolution?
      if (imageHeight != 0 || imageWidth != 0)
      {
          // they are, lets get the original image
          imageFullSize = System.Drawing.Image.FromFile(context.Server.MapPath(imageUrl));

          // now lets check if they are requesting an enlarged image (we don't do that)
          if (imageFullSize.Height <= imageHeight || imageFullSize.Width <= imageWidth)
          {
            // they are, send the original image
            context.Response.ContentType = mimeType;
            context.Response.WriteFile(context.Server.MapPath(imageUrl));
            context.Response.Flush();
            context.Response.End();

            return; 
          }
          else
          {
            // ok it's smaller then the original, now we do support aspect ration based
            // request, so they can only change height or width, and we'll do the calculation so 
            // lets do the calculation. 

            double aspectratio = Convert.ToDouble(Convert.ToDouble(imageFullSize.Width) / Convert.ToDouble(imageFullSize.Height));

            if (imageHeight == 0)
            {
              imageHeight = Convert.ToInt32(imageWidth / aspectratio);
            }
            else if (imageWidth == 0)
            {
              imageWidth = Convert.ToInt32(imageHeight  * aspectratio);
            }

            Debug.Write(imageWidth.ToString() + " " + imageHeight.ToString()); 

            // ok now we have the requested dimensions. lets check to see if we've already generated their request. 
            if (useCache)
            {
              cacheFile = (config.Cache.CacheDirectory + imageUrl.Replace("/", "\\")).Substring(0, (config.Cache.CacheDirectory + imageUrl.Replace("/", "\\")).LastIndexOf(".jpg")) + "\\" + imageHeight + "." + imageWidth + ".jpg"; 

              if (System.IO.File.Exists(cacheFile))
              {
                // we have, lets send that one, no need to regenerate a file we've already generated. 
                context.Response.ContentType = mimeType;
                context.Response.WriteFile(context.Server.MapPath(imageUrl).Substring(0, context.Server.MapPath(imageUrl).LastIndexOf(".jpg")) + "\\" + imageHeight + "." + imageWidth + ".jpg");
                context.Response.Flush();
                context.Response.End();

                imageFullSize.Dispose();

                return;

              }
            }
           
              // ok now comes the dog and pony show, let's resize the image        
              Bitmap mybmp = new Bitmap(imageWidth, imageHeight);
              Graphics objGraphics = Graphics.FromImage(mybmp);

              objGraphics.CompositingMode = CompositingMode.SourceOver;
              objGraphics.CompositingQuality = CompositingQuality.GammaCorrected;

              objGraphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

              objGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
              objGraphics.SmoothingMode = SmoothingMode.HighQuality;

              objGraphics.DrawImage(imageFullSize, 0, 0, mybmp.Width, mybmp.Height);

              Encoder qualityEncoder = Encoder.Quality;
              Encoder colorEncoder = Encoder.ColorDepth;

              EncoderParameter ratio = new EncoderParameter(qualityEncoder, 100L);
              EncoderParameter light = new EncoderParameter(colorEncoder, 24L);
              EncoderParameters codecParameters = new EncoderParameters(2);
              codecParameters.Param[0] = ratio;
              codecParameters.Param[1] = light;

              Single Brightness = 0;
              ColorMatrix BMatrix = new ColorMatrix(new Single[][]
                {new Single[] {1, 0, 0, 0, 0}, 
                 new Single[] {0, 1, 0, 0, 0}, 
                 new Single[] {0, 0, 1, 0, 0}, 
                 new Single[] {0, 0, 0, 1, 0}, 
                 new Single[] {Brightness, Brightness, Brightness, 0, 1}});


              this.ApplyColorMatrix(ref mybmp, BMatrix);


              // now we have to flush this to the outputstream
              mybmp.Save(context.Response.OutputStream, this.GetEncoderInfo(mimeType), codecParameters);

              if (useCache)
              {
                // check to make sure the cache directory exists
                if (!System.IO.Directory.Exists(cacheFile.Substring(0, cacheFile.LastIndexOf("\\"))))
                  System.IO.Directory.CreateDirectory(cacheFile.Substring(0, cacheFile.LastIndexOf("\\")));
                // save the image to the cache directory
                mybmp.Save(cacheFile, this.GetEncoderInfo(mimeType), codecParameters);
              }
              objGraphics.Dispose();
              mybmp.Dispose();

              // send it back to the client
              context.Response.ContentType = mimeType;
              context.Response.Flush();
              context.Response.End();

              return;
            }
          
      }
      else
      {
        // if the file is there and no height or width changes are required. 
        // send the original file
        context.Response.ContentType = mimeType; 
        context.Response.WriteFile(context.Server.MapPath(imageUrl));
        context.Response.Flush();
        context.Response.End(); 
      }

      
    }

    private void ApplyColorMatrix(ref Bitmap myImage, ColorMatrix myMatrix)
    {
        ImageAttributes ImgAtt = new ImageAttributes(); 
        Bitmap bmpMatrix = new Bitmap(myImage.Width, myImage.Height); 
        System.Drawing.Graphics grMatrix = System.Drawing.Graphics.FromImage(bmpMatrix); 
        ImgAtt.SetColorMatrix(myMatrix);

        /*
        Single Gamma = Convert.ToSingle(50 / 50); 
        if (Gamma > 1)
        {
          Gamma += 3 * (Gamma - 1);
        }

        ImgAtt.SetGamma(Gamma);
        */

        grMatrix.DrawImage(myImage, new Rectangle(0, 0, myImage.Width, myImage.Height), 0, 0, myImage.Width, myImage.Height, GraphicsUnit.Pixel, ImgAtt);
        myImage = bmpMatrix;
        grMatrix.Dispose();
        ImgAtt.Dispose();
    }

    private ImageCodecInfo GetEncoderInfo(string mime)
    {
      
        ImageCodecInfo[] encoders;   
        encoders = ImageCodecInfo.GetImageEncoders();
        foreach (ImageCodecInfo e in encoders)
        {
          if (e.MimeType == mime)          
                return e; 
        }

        return null; 

    }
  }
}
